package org.biopax.paxtools.pattern.constraint;

import org.biopax.paxtools.model.BioPAXElement;
import org.biopax.paxtools.pattern.Constraint;
import org.biopax.paxtools.pattern.MappedConst;
import org.biopax.paxtools.pattern.Match;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * Used for using several constraints as a chain. The generated elements from a constraint is used
 * as input by the next constraint in the chain. All constraints except the first one has to be
 * size 2.
 *
 * @author Ozgun Babur
 */
public class ConstraintChain extends ConstraintAdapter
{
	/**
	 * Constraint to be chained.
	 */
	Constraint[] con;

	/**
	 * Constructor with the chained constraints. All constraints has to be generative, and all
	 * constraints, except the first one, has to be size 2. The input to these constraints is the
	 * element that is generated by the previous constraint in the chain.
	 * @param con mapped constraints
	 */
	public ConstraintChain(Constraint... con)
	{
		if (con.length < 2) throw new IllegalArgumentException(
			"Parameter constraint has to be more than one.");

		this.con = con;

		for (int i = 1; i < con.length; i++)
		{
			if (con[i].getVariableSize() > 2) throw new IllegalArgumentException("All chained " +
				"constraints (except first) has to have variable size 2. Unlike con[" + i + "]: "
				+ con[i]);
		}

		setSize(con[0].getVariableSize());
	}

	/**
	 * Chains the member constraints and returns the output of the last constraint.
	 * @param match match to process
	 * @param ind mapped indices
	 * @return satisfying elements
	 */
	@Override
	public Collection<BioPAXElement> generate(Match match, int... ind)
	{

		Collection<BioPAXElement> gen = new HashSet<BioPAXElement> (
			con[0].generate(match, ind));

		Set<BioPAXElement> input = new HashSet<BioPAXElement>();
		Set<BioPAXElement> output = new HashSet<BioPAXElement>(gen);
		int[] tempInd = {0, 1};

		for (int i = 1; i < con.length; i++)
		{
			if (output.isEmpty()) break;
			input.clear();
			input.addAll(output);
			output.clear();

			for (BioPAXElement ele : input)
			{
				Match m = new Match(2);
				m.set(ele, 0);
				output.addAll(con[i].generate(m, tempInd));
			}
		}

		return output;
	}
}
